V4L/DVB (8812): gspca: Do pac73xx webcams work.
Hans de Goede [Wed, 3 Sep 2008 20:12:14 +0000 (17:12 -0300)]
- documentation for some registers
- some preparations for adding autogain_n_exposure functionality
- various pac7311 fixes
- disable brightness and colors controls for 7311
- fix contrast control for 7311
- add hflip and vflip controls for 7311
- minimal jpeg header
- proper SOF detection

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

drivers/media/video/gspca/pac207.c
drivers/media/video/gspca/pac7311.c
drivers/media/video/gspca/pac_common.h [new file with mode: 0644]

index d9668f4..620c963 100644 (file)
@@ -181,9 +181,6 @@ static const __u8 pac207_sensor_init[][8] = {
                        /* 48 reg_72 Rate Control end BalSize_4a =0x36 */
 static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };
 
-static const unsigned char pac207_sof_marker[5] =
-               { 0xff, 0xff, 0x00, 0xff, 0x96 };
-
 static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
        const u8 *buffer, u16 length)
 {
@@ -367,31 +364,8 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
                sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
 }
 
-static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
-                                       unsigned char *m, int len)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i;
-
-       /* Search for the SOF marker (fixed part) in the header */
-       for (i = 0; i < len; i++) {
-               if (m[i] == pac207_sof_marker[sd->sof_read]) {
-                       sd->sof_read++;
-                       if (sd->sof_read == sizeof(pac207_sof_marker)) {
-                               PDEBUG(D_STREAM,
-                                       "SOF found, bytes to analyze: %u."
-                                       " Frame starts at byte #%u",
-                                       len, i + 1);
-                               sd->sof_read = 0;
-                               return m + i + 1;
-                       }
-               } else {
-                       sd->sof_read = 0;
-               }
-       }
-
-       return NULL;
-}
+/* Include pac common sof detection functions */
+#include "pac_common.h"
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        struct gspca_frame *frame,
@@ -401,14 +375,14 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
        unsigned char *sof;
 
-       sof = pac207_find_sof(gspca_dev, data, len);
+       sof = pac_find_sof(gspca_dev, data, len);
        if (sof) {
                int n;
 
                /* finish decoding current frame */
                n = sof - data;
-               if (n > sizeof pac207_sof_marker)
-                       n -= sizeof pac207_sof_marker;
+               if (n > sizeof pac_sof_marker)
+                       n -= sizeof pac_sof_marker;
                else
                        n = 0;
                frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
index 4cc9379..bed04cc 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+/* Some documentation about various registers as determined by trial and error.
+   When the register addresses differ between the 7202 and the 7311 the 2
+   different addresses are written as 7302addr/7311addr, when one of the 2
+   addresses is a - sign that register description is not valid for the
+   matching IC.
+
+   Register page 1:
+
+   Address     Description
+   -/0x08      Unknown compressor related, must always be 8 except when not
+               in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+   -/0x1b      Auto white balance related, bit 0 is AWB enable (inverted)
+               bits 345 seem to toggle per color gains on/off (inverted)
+   0x78                Global control, bit 6 controls the LED (inverted)
+   -/0x80      JPEG compression ratio ? Best not touched
+
+   Register page 3/4:
+
+   Address     Description
+   0x02                Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+               the 7302, so one of 3, 6, 9, ...
+   -/0x0f      Master gain 1-245, low value = high gain
+   0x10/-      Master gain 0-31
+   -/0x10      Another gain 0-15, limited influence (1-2x gain I guess)
+   0x21                Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+*/
+
 #define MODULE_NAME "pac7311"
 
 #include "gspca.h"
-#include "jpeg.h"
 
 MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
 MODULE_DESCRIPTION("Pixart PAC7311");
@@ -32,25 +58,22 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       int lum_sum;
-       atomic_t avg_lum;
-       atomic_t do_gain;
-
        unsigned char brightness;
        unsigned char contrast;
        unsigned char colors;
        unsigned char autogain;
        __u8 hflip;
        __u8 vflip;
-       __u8 qindex;
-
-       char tosof;     /* number of bytes before next start of frame */
-       signed char ag_cnt;
-#define AG_CNT_START 13
 
        __u8 sensor;
 #define SENSOR_PAC7302 0
 #define SENSOR_PAC7311 1
+
+       u8 sof_read;
+       u8 header_read;
+       u8 autogain_ignore_frames;
+
+       atomic_t avg_lum;
 };
 
 /* V4L2 controls supported by the driver */
@@ -92,7 +115,7 @@ static struct ctrl sd_ctrls[] = {
 #define CONTRAST_MAX 255
                .maximum = CONTRAST_MAX,
                .step    = 1,
-#define CONTRAST_DEF 60
+#define CONTRAST_DEF 127
                .default_value = CONTRAST_DEF,
            },
            .set = sd_setcontrast,
@@ -243,7 +266,7 @@ static const __u8 start_7302[] = {
        0x2a, 5,        0xc8, 0x00, 0x18, 0x12, 0x22,
        0x64, 8,        0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
        0x6e, 1,        0x08,
-       0xff, 1,        0x03,           /* page 1 */
+       0xff, 1,        0x01,           /* page 1 */
        0x78, 1,        0x00,
        0, 0                            /* end of sequence */
 };
@@ -274,9 +297,9 @@ static const __u8 page3_7302[] = {
 
 /* pac 7311 */
 static const __u8 probe_7311[] = {
-       0x78, 0x40,     /* Bit_0=start stream, Bit_7=LED */
-       0x78, 0x40,     /* Bit_0=start stream, Bit_7=LED */
-       0x78, 0x44,     /* Bit_0=start stream, Bit_7=LED */
+       0x78, 0x40,     /* Bit_0=start stream, Bit_6=LED */
+       0x78, 0x40,     /* Bit_0=start stream, Bit_6=LED */
+       0x78, 0x44,     /* Bit_0=start stream, Bit_6=LED */
        0xff, 0x04,
        0x27, 0x80,
        0x28, 0xca,
@@ -340,18 +363,6 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
                        500);
 }
 
-static __u8 reg_r(struct gspca_dev *gspca_dev,
-                            __u8 index)
-{
-       usb_control_msg(gspca_dev->dev,
-                       usb_rcvctrlpipe(gspca_dev->dev, 0),
-                       0,                      /* request */
-                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                       0,                      /* value */
-                       index, gspca_dev->usb_buf, 1,
-                       500);
-       return gspca_dev->usb_buf[0];
-}
 
 static void reg_w(struct gspca_dev *gspca_dev,
                  __u8 index,
@@ -413,7 +424,7 @@ static void reg_w_var(struct gspca_dev *gspca_dev,
                        reg_w_page(gspca_dev, page3_7302, sizeof page3_7302);
                        break;
                default:
-                       if (len > 32) {
+                       if (len > 64) {
                                PDEBUG(D_ERR|D_STREAM,
                                        "Incorrect variable sequence");
                                return;
@@ -453,7 +464,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->nmodes = 1;
        } else {
                PDEBUG(D_CONF, "Find Sensor PAC7311");
-               reg_w_seq(gspca_dev, probe_7302, sizeof probe_7302);
+               reg_w_seq(gspca_dev, probe_7311, sizeof probe_7311);
 
                cam->cam_mode = vga_mode;
                cam->nmodes = ARRAY_SIZE(vga_mode);
@@ -465,8 +476,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->autogain = AUTOGAIN_DEF;
        sd->hflip = HFLIP_DEF;
        sd->vflip = VFLIP_DEF;
-       sd->qindex = 3;
-       sd->ag_cnt = -1;
        return 0;
 }
 
@@ -497,6 +506,7 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0xdc, 0x01);
 }
 
+/* This function is used by pac7302 only */
 static void setbrightness(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -506,7 +516,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                setbrightcont(gspca_dev);
                return;
        }
-/*jfm: inverted?*/
+/* HDG: this is not brightness but gain, I'll add gain and exposure controls
+   in a next patch */
+       return;
+
        brightness = BRIGHTNESS_MAX - sd->brightness;
        reg_w(gspca_dev, 0xff, 0x04);
        reg_w(gspca_dev, 0x0e, 0x00);
@@ -524,12 +537,13 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                setbrightcont(gspca_dev);
                return;
        }
-       reg_w(gspca_dev, 0xff, 0x01);
-       reg_w(gspca_dev, 0x10, sd->contrast);
+       reg_w(gspca_dev, 0xff, 0x04);
+       reg_w(gspca_dev, 0x10, sd->contrast >> 4);
        /* load registers to sensor (Bit 0, auto clear) */
        reg_w(gspca_dev, 0x11, 0x01);
 }
 
+/* This function is used by pac7302 only */
 static void setcolors(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -551,36 +565,24 @@ static void setcolors(struct gspca_dev *gspca_dev)
                        reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
                }
                reg_w(gspca_dev, 0xdc, 0x01);
-               return;
+               PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
        }
-       reg_w(gspca_dev, 0xff, 0x01);
-       reg_w(gspca_dev, 0x80, sd->colors);
-       /* load registers to sensor (Bit 0, auto clear) */
-       reg_w(gspca_dev, 0x11, 0x01);
-       PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (sd->autogain) {
-               sd->lum_sum = 0;
-               sd->ag_cnt = AG_CNT_START;
-       } else {
-               sd->ag_cnt = -1;
-       }
-}
-
-/* this function is used by pac7302 only */
 static void sethvflip(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        __u8 data;
 
-       reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
-       data = (sd->hflip ? 0x00 : 0x08)
-               | (sd->vflip ? 0x04 : 0x00);
+       if (sd->sensor == SENSOR_PAC7302) {
+               reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
+               data = (sd->hflip ? 0x00 : 0x08)
+                       | (sd->vflip ? 0x04 : 0x00);
+       } else {
+               reg_w(gspca_dev, 0xff, 0x04);           /* page 3 */
+               data = (sd->hflip ? 0x04 : 0x00)
+                       | (sd->vflip ? 0x08 : 0x00);
+       }
        reg_w(gspca_dev, 0x21, data);
        reg_w(gspca_dev, 0x11, 0x01);
 }
@@ -588,7 +590,6 @@ static void sethvflip(struct gspca_dev *gspca_dev)
 /* this function is called at open time */
 static int sd_open(struct gspca_dev *gspca_dev)
 {
-       reg_w(gspca_dev, 0x78, 0x44);   /* Turn on LED */
        return 0;
 }
 
@@ -596,7 +597,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       sd->tosof = 0;
+       sd->sof_read = 0;
 
        if (sd->sensor == SENSOR_PAC7302)
                reg_w_var(gspca_dev, start_7302);
@@ -606,7 +607,6 @@ static void sd_start(struct gspca_dev *gspca_dev)
        setcontrast(gspca_dev);
        setbrightness(gspca_dev);
        setcolors(gspca_dev);
-       setautogain(gspca_dev);
 
        /* set correct resolution */
        switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
@@ -621,7 +621,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
                break;
        case 1:                                 /* 320x240 pac7311 */
                reg_w(gspca_dev, 0xff, 0x04);
-               reg_w(gspca_dev, 0x02, 0x07);
+               reg_w(gspca_dev, 0x02, 0x03);
                reg_w(gspca_dev, 0xff, 0x01);
                reg_w(gspca_dev, 0x08, 0x09);
                reg_w(gspca_dev, 0x17, 0x30);
@@ -650,6 +650,10 @@ static void sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x78, 0x44);
                reg_w(gspca_dev, 0x78, 0x45);
        }
+
+       sd->sof_read = 0;
+       sd->autogain_ignore_frames = 0;
+       atomic_set(&sd->avg_lum, -1);
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -657,6 +661,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->sensor == SENSOR_PAC7302) {
+               reg_w(gspca_dev, 0xff, 0x01);
                reg_w(gspca_dev, 0x78, 0x00);
                reg_w(gspca_dev, 0x78, 0x00);
                return;
@@ -668,9 +673,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x2a, 0x0e);
        reg_w(gspca_dev, 0xff, 0x01);
        reg_w(gspca_dev, 0x3e, 0x20);
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
 }
 
 static void sd_stop0(struct gspca_dev *gspca_dev)
@@ -690,42 +695,22 @@ static void sd_close(struct gspca_dev *gspca_dev)
 
 static void do_autogain(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int luma;
-       int luma_mean = 128;
-       int luma_delta = 20;
-       __u8 spring = 5;
-       int Gbright;
-
-       if (!atomic_read(&sd->do_gain))
-               return;
-       atomic_set(&sd->do_gain, 0);
-
-       luma = atomic_read(&sd->avg_lum);
-       Gbright = reg_r(gspca_dev, 0x02);
-       PDEBUG(D_FRAM, "luma mean %d", luma);
-       if (luma < luma_mean - luma_delta ||
-           luma > luma_mean + luma_delta) {
-               Gbright += (luma_mean - luma) >> spring;
-               if (Gbright > 0x1a)
-                       Gbright = 0x1a;
-               else if (Gbright < 4)
-                       Gbright = 4;
-               PDEBUG(D_FRAM, "gbright %d", Gbright);
-               if (sd->sensor == SENSOR_PAC7302) {
-                       reg_w(gspca_dev, 0xff, 0x03);
-                       reg_w(gspca_dev, 0x10, Gbright);
-                       /* load registers to sensor (Bit 0, auto clear) */
-                       reg_w(gspca_dev, 0x11, 0x01);
-               } else {
-                       reg_w(gspca_dev, 0xff, 0x04);
-                       reg_w(gspca_dev, 0x0f, Gbright);
-                       /* load registers to sensor (Bit 0, auto clear) */
-                       reg_w(gspca_dev, 0x11, 0x01);
-               }
-       }
 }
 
+static const unsigned char pac7311_jpeg_header1[] = {
+  0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08
+};
+
+static const unsigned char pac7311_jpeg_header2[] = {
+  0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda,
+  0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+};
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+#define HEADER_LENGTH 2
+
 /* this function is run at interrupt level */
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        struct gspca_frame *frame,      /* target */
@@ -733,58 +718,89 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int i;
-
-#define INTER_FRAME 0x53       /* eof + inter frame + sof */
-#define LUM_OFFSET 0x1e                /* reverse offset / start of frame */
-
-       /*
-        * inside a frame, there may be:
-        *      escaped ff ('ff 00')
-        *      sequences'ff ff ff xx' to remove
-        *      end of frame ('ff d9')
-        * at the end of frame, there are:
-        *      ff d9                   end of frame
-        *      0x33 bytes
-        *      one byte luminosity
-        *      0x16 bytes
-        *      ff ff 00 ff 96 62 44    start of frame
-        */
-
-       if (sd->tosof != 0) {   /* if outside a frame */
-
-               /* get the luminosity and go to the start of frame */
-               data += sd->tosof;
-               len -= sd->tosof;
-               if (sd->tosof > LUM_OFFSET)
-                       sd->lum_sum += data[-LUM_OFFSET];
-               sd->tosof = 0;
-               jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21);
+       unsigned char *sof;
+
+       sof = pac_find_sof(gspca_dev, data, len);
+       if (sof) {
+               unsigned char tmpbuf[4];
+               int n, lum_offset, footer_length;
+
+               if (sd->sensor == SENSOR_PAC7302) {
+                 lum_offset = 34 + sizeof pac_sof_marker;
+                 footer_length = 74;
+               } else {
+                 lum_offset = 24 + sizeof pac_sof_marker;
+                 footer_length = 26;
+               }
+
+               /* Finish decoding current frame */
+               n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+               if (n < 0) {
+                       frame->data_end += n;
+                       n = 0;
+               }
+               frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                                       data, n);
+               if (gspca_dev->last_packet_type != DISCARD_PACKET &&
+                               frame->data_end[-2] == 0xff &&
+                               frame->data_end[-1] == 0xd9)
+                       frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                               NULL, 0);
+
+               n = sof - data;
+               len -= n;
+               data = sof;
+
+               /* Get average lumination */
+               if (gspca_dev->last_packet_type == LAST_PACKET &&
+                               n >= lum_offset) {
+                       if (sd->sensor == SENSOR_PAC7302)
+                               atomic_set(&sd->avg_lum,
+                                               (data[-lum_offset] << 8) |
+                                               data[-lum_offset + 1]);
+                       else
+                               atomic_set(&sd->avg_lum,
+                                               data[-lum_offset] +
+                                               data[-lum_offset + 1]);
+               } else {
+                       atomic_set(&sd->avg_lum, -1);
+               }
+
+               /* Start the new frame with the jpeg header */
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                       pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1));
+               if (sd->sensor == SENSOR_PAC7302) {
+                       /* The PAC7302 has the image rotated 90 degrees */
+                       tmpbuf[0] = gspca_dev->width >> 8;
+                       tmpbuf[1] = gspca_dev->width & 0xff;
+                       tmpbuf[2] = gspca_dev->height >> 8;
+                       tmpbuf[3] = gspca_dev->height & 0xff;
+               } else {
+                       tmpbuf[0] = gspca_dev->height >> 8;
+                       tmpbuf[1] = gspca_dev->height & 0xff;
+                       tmpbuf[2] = gspca_dev->width >> 8;
+                       tmpbuf[3] = gspca_dev->width & 0xff;
+               }
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4);
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2));
+
+               sd->header_read = 0;
        }
 
-       for (i = 0; i < len; i++) {
-               if (data[i] != 0xff)
-                       continue;
-               switch (data[i + 1]) {
-               case 0xd9:              /* 'ff d9' end of frame */
-                       frame = gspca_frame_add(gspca_dev,
-                                               LAST_PACKET,
-                                               frame, data, i + 2);
-                       data += i + INTER_FRAME;
-                       len -= i + INTER_FRAME;
-                       i = 0;
-                       if (len > -LUM_OFFSET)
-                               sd->lum_sum += data[-LUM_OFFSET];
-                       if (len < 0) {
-                               sd->tosof = -len;
-                               break;
-                       }
-                       jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21);
-                       break;
+       if (sd->header_read < HEADER_LENGTH) {
+               /* skip the variable part of the sof header */
+               int needed = HEADER_LENGTH - sd->header_read;
+               if (len <= needed) {
+                       sd->header_read += len;
+                       return;
                }
+               data += needed;
+               len -= needed;
+               sd->header_read = HEADER_LENGTH;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET,
-                       frame, data, i);
+
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -846,8 +862,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->autogain = val;
-       if (gspca_dev->streaming)
-               setautogain(gspca_dev);
+
        return 0;
 }
 
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h
new file mode 100644 (file)
index 0000000..a19b5d4
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Pixart PAC207BCA / PAC73xx common functions
+ *
+ * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+static const unsigned char pac_sof_marker[5] =
+               { 0xff, 0xff, 0x00, 0xff, 0x96 };
+
+static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev,
+                                       unsigned char *m, int len)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+
+       /* Search for the SOF marker (fixed part) in the header */
+       for (i = 0; i < len; i++) {
+               if (m[i] == pac_sof_marker[sd->sof_read]) {
+                       sd->sof_read++;
+                       if (sd->sof_read == sizeof(pac_sof_marker)) {
+                               PDEBUG(D_STREAM,
+                                       "SOF found, bytes to analyze: %u."
+                                       " Frame starts at byte #%u",
+                                       len, i + 1);
+                               sd->sof_read = 0;
+                               return m + i + 1;
+                       }
+               } else {
+                       sd->sof_read = 0;
+               }
+       }
+
+       return NULL;
+}