Create a separate function for parsing UVC payload headers and extract code
from other functions into it. Store the parsed values in a header struct.

Signed-off-by: Pawel Osciak <posc...@chromium.org>
---
 drivers/media/usb/uvc/uvc_video.c | 270 +++++++++++++++++++-------------------
 drivers/media/usb/uvc/uvcvideo.h  |  21 +++
 2 files changed, 157 insertions(+), 134 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c 
b/drivers/media/usb/uvc/uvc_video.c
index 2f9a5fa..59f57a2 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -422,40 +422,14 @@ static int uvc_commit_video(struct uvc_streaming *stream,
 
 static void
 uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
-                      const __u8 *data, int len)
+                       struct uvc_payload_header *header)
 {
        struct uvc_clock_sample *sample;
-       unsigned int header_size;
-       bool has_pts = false;
-       bool has_scr = false;
        unsigned long flags;
        struct timespec ts;
        u16 host_sof;
        u16 dev_sof;
 
-       switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
-       case UVC_STREAM_PTS | UVC_STREAM_SCR:
-               header_size = 12;
-               has_pts = true;
-               has_scr = true;
-               break;
-       case UVC_STREAM_PTS:
-               header_size = 6;
-               has_pts = true;
-               break;
-       case UVC_STREAM_SCR:
-               header_size = 8;
-               has_scr = true;
-               break;
-       default:
-               header_size = 2;
-               break;
-       }
-
-       /* Check for invalid headers. */
-       if (len < header_size)
-               return;
-
        /* Extract the timestamps:
         *
         * - store the frame PTS in the buffer structure
@@ -463,17 +437,17 @@ uvc_video_clock_decode(struct uvc_streaming *stream, 
struct uvc_buffer *buf,
         *   kernel timestamps and store them with the SCR STC and SOF fields
         *   in the ring buffer
         */
-       if (has_pts && buf != NULL)
-               buf->pts = get_unaligned_le32(&data[2]);
+       if (header->has_pts && buf != NULL)
+               buf->pts = header->pts;
 
-       if (!has_scr)
+       if (!header->has_scr)
                return;
 
        /* To limit the amount of data, drop SCRs with an SOF identical to the
         * previous one.
         */
-       dev_sof = get_unaligned_le16(&data[header_size - 2]);
-       if (dev_sof == stream->clock.last_sof)
+       dev_sof = header->sof;
+       if (dev_sof <= stream->clock.last_sof)
                return;
 
        stream->clock.last_sof = dev_sof;
@@ -513,7 +487,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct 
uvc_buffer *buf,
        spin_lock_irqsave(&stream->clock.lock, flags);
 
        sample = &stream->clock.samples[stream->clock.head];
-       sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
+       sample->dev_stc = header->stc;
        sample->dev_sof = dev_sof;
        sample->host_sof = host_sof;
        sample->host_ts = ts;
@@ -756,114 +730,74 @@ done:
  */
 
 static void uvc_video_stats_decode(struct uvc_streaming *stream,
-               const __u8 *data, int len)
+                                   struct uvc_payload_header *header)
 {
-       unsigned int header_size;
-       bool has_pts = false;
-       bool has_scr = false;
-       u16 uninitialized_var(scr_sof);
-       u32 uninitialized_var(scr_stc);
-       u32 uninitialized_var(pts);
-
        if (stream->stats.stream.nb_frames == 0 &&
            stream->stats.frame.nb_packets == 0)
                ktime_get_ts(&stream->stats.stream.start_ts);
 
-       switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
-       case UVC_STREAM_PTS | UVC_STREAM_SCR:
-               header_size = 12;
-               has_pts = true;
-               has_scr = true;
-               break;
-       case UVC_STREAM_PTS:
-               header_size = 6;
-               has_pts = true;
-               break;
-       case UVC_STREAM_SCR:
-               header_size = 8;
-               has_scr = true;
-               break;
-       default:
-               header_size = 2;
-               break;
-       }
-
-       /* Check for invalid headers. */
-       if (len < header_size || data[0] < header_size) {
-               stream->stats.frame.nb_invalid++;
-               return;
-       }
-
-       /* Extract the timestamps. */
-       if (has_pts)
-               pts = get_unaligned_le32(&data[2]);
-
-       if (has_scr) {
-               scr_stc = get_unaligned_le32(&data[header_size - 6]);
-               scr_sof = get_unaligned_le16(&data[header_size - 2]);
-       }
-
        /* Is PTS constant through the whole frame ? */
-       if (has_pts && stream->stats.frame.nb_pts) {
-               if (stream->stats.frame.pts != pts) {
+       if (header->has_pts && stream->stats.frame.nb_pts) {
+               if (stream->stats.frame.pts != header->pts) {
                        stream->stats.frame.nb_pts_diffs++;
                        stream->stats.frame.last_pts_diff =
                                stream->stats.frame.nb_packets;
                }
        }
 
-       if (has_pts) {
+       if (header->has_pts) {
                stream->stats.frame.nb_pts++;
-               stream->stats.frame.pts = pts;
+               stream->stats.frame.pts = header->pts;
        }
 
        /* Do all frames have a PTS in their first non-empty packet, or before
         * their first empty packet ?
         */
        if (stream->stats.frame.size == 0) {
-               if (len > header_size)
-                       stream->stats.frame.has_initial_pts = has_pts;
-               if (len == header_size && has_pts)
+               if (header->payload_size > 0)
+                       stream->stats.frame.has_initial_pts = header->has_pts;
+               if (header->payload_size == 0 && header->has_pts)
                        stream->stats.frame.has_early_pts = true;
        }
 
        /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
-       if (has_scr && stream->stats.frame.nb_scr) {
-               if (stream->stats.frame.scr_stc != scr_stc)
+       if (header->has_scr && stream->stats.frame.nb_scr) {
+               if (stream->stats.frame.scr_stc != header->stc)
                        stream->stats.frame.nb_scr_diffs++;
        }
 
-       if (has_scr) {
+       if (header->has_scr) {
                /* Expand the SOF counter to 32 bits and store its value. */
                if (stream->stats.stream.nb_frames > 0 ||
                    stream->stats.frame.nb_scr > 0)
                        stream->stats.stream.scr_sof_count +=
-                               (scr_sof - stream->stats.stream.scr_sof) % 2048;
-               stream->stats.stream.scr_sof = scr_sof;
+                               (header->sof - stream->stats.stream.scr_sof)
+                               % 2048;
+               stream->stats.stream.scr_sof = header->sof;
 
                stream->stats.frame.nb_scr++;
-               stream->stats.frame.scr_stc = scr_stc;
-               stream->stats.frame.scr_sof = scr_sof;
+               stream->stats.frame.scr_stc = header->stc;
+               stream->stats.frame.scr_sof = header->sof;
 
-               if (scr_sof < stream->stats.stream.min_sof)
-                       stream->stats.stream.min_sof = scr_sof;
-               if (scr_sof > stream->stats.stream.max_sof)
-                       stream->stats.stream.max_sof = scr_sof;
+               if (header->sof < stream->stats.stream.min_sof)
+                       stream->stats.stream.min_sof = header->sof;
+               if (header->sof > stream->stats.stream.max_sof)
+                       stream->stats.stream.max_sof = header->sof;
        }
 
        /* Record the first non-empty packet number. */
-       if (stream->stats.frame.size == 0 && len > header_size)
+       if (stream->stats.frame.size == 0 && header->payload_size > 0)
                stream->stats.frame.first_data = stream->stats.frame.nb_packets;
 
        /* Update the frame size. */
-       stream->stats.frame.size += len - header_size;
+       stream->stats.frame.size += header->payload_size;
 
        /* Update the packets counters. */
        stream->stats.frame.nb_packets++;
-       if (len > header_size)
+       if (header->payload_size == 0)
                stream->stats.frame.nb_empty++;
 
-       if (data[1] & UVC_STREAM_ERR)
+       if (header->has_err)
                stream->stats.frame.nb_errors++;
 }
 
@@ -1006,21 +940,9 @@ static void uvc_video_stats_stop(struct uvc_streaming 
*stream)
  * uvc_video_decode_end will never be called with a NULL buffer.
  */
 static int uvc_video_decode_start(struct uvc_streaming *stream,
-               struct uvc_buffer *buf, const __u8 *data, int len)
+               struct uvc_buffer *buf, struct uvc_payload_header *header)
 {
-       __u8 fid;
-
-       /* Sanity checks:
-        * - packet must be at least 2 bytes long
-        * - bHeaderLength value must be at least 2 bytes (see above)
-        * - bHeaderLength value can't be larger than the packet size.
-        */
-       if (len < 2 || data[0] < 2 || data[0] > len) {
-               stream->stats.frame.nb_invalid++;
-               return -EINVAL;
-       }
-
-       fid = data[1] & UVC_STREAM_FID;
+       u8 fid = header->fid;
 
        /* Increase the sequence number regardless of any buffer states, so
         * that discontinuous sequence numbers always indicate lost frames.
@@ -1031,8 +953,8 @@ static int uvc_video_decode_start(struct uvc_streaming 
*stream,
                        uvc_video_stats_update(stream);
        }
 
-       uvc_video_clock_decode(stream, buf, data, len);
-       uvc_video_stats_decode(stream, data, len);
+       uvc_video_clock_decode(stream, buf, header);
+       uvc_video_stats_decode(stream, header);
 
        /* Store the payload FID bit and return immediately when the buffer is
         * NULL.
@@ -1043,7 +965,7 @@ static int uvc_video_decode_start(struct uvc_streaming 
*stream,
        }
 
        /* Mark the buffer as bad if the error bit is set. */
-       if (data[1] & UVC_STREAM_ERR) {
+       if (header->has_err) {
                uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
                          "set).\n");
                buf->error = 1;
@@ -1064,7 +986,7 @@ static int uvc_video_decode_start(struct uvc_streaming 
*stream,
                        uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
                                "sync).\n");
                        if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
-                           (data[1] & UVC_STREAM_EOF))
+                           (header->has_eof))
                                stream->last_fid ^= UVC_STREAM_FID;
                        return -ENODATA;
                }
@@ -1107,7 +1029,7 @@ static int uvc_video_decode_start(struct uvc_streaming 
*stream,
 
        stream->last_fid = fid;
 
-       return data[0];
+       return 0;
 }
 
 static void uvc_video_decode_data(struct uvc_streaming *stream,
@@ -1128,18 +1050,20 @@ static void uvc_video_decode_data(struct uvc_streaming 
*stream,
 
        /* Complete the current frame if the buffer size was exceeded. */
        if (len > maxlen) {
-               uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+               uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow) "
+                               "len=%d, buffer size=%d used=%d\n",
+                               len, buf->length, buf->bytesused);
                buf->state = UVC_BUF_STATE_READY;
        }
 }
 
 static void uvc_video_decode_end(struct uvc_streaming *stream,
-               struct uvc_buffer *buf, const __u8 *data, int len)
+               struct uvc_buffer *buf, struct uvc_payload_header *header)
 {
        /* Mark the buffer as done if the EOF marker is set. */
-       if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
+       if (header->has_eof && buf->bytesused != 0) {
                uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
-               if (data[0] == len)
+               if (header->payload_size == 0)
                        uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
                buf->state = UVC_BUF_STATE_READY;
                if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
@@ -1186,6 +1110,75 @@ static int uvc_video_encode_data(struct uvc_streaming 
*stream,
        return nbytes;
 }
 
+static int uvc_video_parse_header(struct uvc_streaming *stream,
+               const __u8 *data, int len, struct uvc_payload_header *header)
+{
+       int off = 2;
+
+       /* Sanity checks:
+        * - packet must be at least 2 bytes long
+        * - bHeaderLength value must be at least 2 bytes (see above)
+        */
+       if (len < 2 || data[0] < 2)
+               goto error;
+
+       header->length = 2; /* 1 byte of header length + 1 byte of BFH. */
+
+       header->has_sli = false;
+       header->has_eof = data[1] & UVC_STREAM_EOF;
+       header->has_pts = data[1] & UVC_STREAM_PTS;
+       header->has_scr = data[1] & UVC_STREAM_SCR;
+       header->has_err = data[1] & UVC_STREAM_ERR;
+
+       if (header->has_pts)
+               header->length += 4;
+
+       if (header->has_scr)
+               header->length += 6;
+
+       if (stream->cur_format->fcc == V4L2_PIX_FMT_VP8) {
+               /* VP8 payload has 2 additional bytes of BFH. */
+               header->length += 2;
+               off += 2;
+
+               /* SLI always present for VP8 simulcast (at the end of header),
+                * allowed for VP8 non-simulcast.
+                */
+               header->has_sli = data[1] & UVC_STREAM_SLI;
+               if (header->has_sli)
+                       header->length += 2;
+       }
+
+       /* - bHeaderLength value can't be larger than the packet size. */
+       if (len < data[0] || data[0] != header->length)
+               goto error;
+
+       /* PTS 4 bytes, STC 4 bytes, SOF 2 bytes. */
+       if (header->has_pts) {
+               header->pts = get_unaligned_le32(&data[off]);
+               off += 4;
+       }
+
+       if (header->has_scr) {
+               header->stc = get_unaligned_le32(&data[off]);
+               off += 4;
+               header->sof = get_unaligned_le16(&data[off]);
+               off += 2;
+       }
+
+       if (header->has_sli)
+               header->sli = get_unaligned_le16(&data[off]);
+
+       header->payload_size = len - header->length;
+       header->fid = data[1] & UVC_STREAM_FID;
+
+       return 0;
+
+error:
+       stream->stats.frame.nb_invalid++;
+       return -EINVAL;
+}
+
 /* ------------------------------------------------------------------------
  * URB handling
  */
@@ -1195,9 +1188,11 @@ static int uvc_video_encode_data(struct uvc_streaming 
*stream,
  */
 static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming 
*stream)
 {
+       unsigned int len;
        u8 *mem;
        int ret, i;
        struct uvc_buffer *buf = NULL;
+       struct uvc_payload_header header;
 
        for (i = 0; i < urb->number_of_packets; ++i) {
                if (urb->iso_frame_desc[i].status < 0) {
@@ -1209,12 +1204,16 @@ static void uvc_video_decode_isoc(struct urb *urb, 
struct uvc_streaming *stream)
                        continue;
                }
 
-               /* Decode the payload header. */
                mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               len = urb->iso_frame_desc[i].actual_length;
+
+               ret = uvc_video_parse_header(stream, mem, len, &header);
+               if (ret < 0)
+                       continue;
+
                buf = uvc_queue_get_first_buf(&stream->queue);
                do {
-                       ret = uvc_video_decode_start(stream, buf, mem,
-                               urb->iso_frame_desc[i].actual_length);
+                       ret = uvc_video_decode_start(stream, buf, &header);
                        if (ret == -EAGAIN)
                                buf = uvc_queue_next_buffer(&stream->queue,
                                                            buf);
@@ -1224,12 +1223,11 @@ static void uvc_video_decode_isoc(struct urb *urb, 
struct uvc_streaming *stream)
                        continue;
 
                /* Decode the payload data. */
-               uvc_video_decode_data(stream, buf, mem + ret,
-                       urb->iso_frame_desc[i].actual_length - ret);
+               uvc_video_decode_data(stream, buf, mem + header.length,
+                       urb->iso_frame_desc[i].actual_length - header.length);
 
                /* Process the header again. */
-               uvc_video_decode_end(stream, buf, mem,
-                       urb->iso_frame_desc[i].actual_length);
+               uvc_video_decode_end(stream, buf, &header);
 
                if (buf->state == UVC_BUF_STATE_READY) {
                        if (buf->length != buf->bytesused &&
@@ -1246,6 +1244,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct 
uvc_streaming *stream)
 {
        u8 *mem;
        int len, ret;
+       struct uvc_payload_header header;
        struct uvc_buffer *buf;
 
        /*
@@ -1259,6 +1258,10 @@ static void uvc_video_decode_bulk(struct urb *urb, 
struct uvc_streaming *stream)
        len = urb->actual_length;
        stream->bulk.payload_size += len;
 
+       ret = uvc_video_parse_header(stream, mem, len, &header);
+       if (ret < 0)
+               return;
+
        buf = uvc_queue_get_first_buf(&stream->queue);
 
        /* If the URB is the first of its payload, decode and save the
@@ -1266,7 +1269,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct 
uvc_streaming *stream)
         */
        if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
                do {
-                       ret = uvc_video_decode_start(stream, buf, mem, len);
+                       ret = uvc_video_decode_start(stream, buf, &header);
                        if (ret == -EAGAIN)
                                buf = uvc_queue_next_buffer(&stream->queue,
                                                            buf);
@@ -1276,11 +1279,11 @@ static void uvc_video_decode_bulk(struct urb *urb, 
struct uvc_streaming *stream)
                if (ret < 0 || buf == NULL) {
                        stream->bulk.skip_payload = 1;
                } else {
-                       memcpy(stream->bulk.header, mem, ret);
-                       stream->bulk.header_size = ret;
+                       memcpy(stream->bulk.header, mem, header.length);
+                       stream->bulk.header_size = header.length;
 
-                       mem += ret;
-                       len -= ret;
+                       mem += header.length;
+                       len -= header.length;
                }
        }
 
@@ -1299,8 +1302,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct 
uvc_streaming *stream)
        if (urb->actual_length < urb->transfer_buffer_length ||
            stream->bulk.payload_size >= stream->bulk.max_payload_size) {
                if (!stream->bulk.skip_payload && buf != NULL) {
-                       uvc_video_decode_end(stream, buf, stream->bulk.header,
-                               stream->bulk.payload_size);
+                       uvc_video_decode_end(stream, buf, &header);
                        if (buf->state == UVC_BUF_STATE_READY)
                                buf = uvc_queue_next_buffer(&stream->queue,
                                                            buf);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index bca8715..b355b2c 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -453,6 +453,27 @@ struct uvc_stats_stream {
        unsigned int max_sof;           /* Maximum STC.SOF value */
 };
 
+struct uvc_payload_header {
+       bool has_eof;
+
+       bool has_pts;
+       u32 pts;
+
+       bool has_scr;
+       u16 sof;
+       u32 stc;
+
+       bool has_sli;
+       u16 sli;
+
+       u8 fid;
+
+       bool has_err;
+
+       int length;
+       int payload_size;
+};
+
 struct uvc_streaming {
        struct list_head list;
        struct uvc_device *dev;
-- 
1.8.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to