V4L/DVB (8604): gspca: Fix of "scheduling while atomic" crash.
Jean-Francois Moine [Sun, 3 Aug 2008 10:52:53 +0000 (07:52 -0300)]
The crash is due to USB exchanges done at interrupt level.
These exchanges, tied to autogain, are now done by the application.
Also, there is a fix about autogain start.
Concerned subdrivers: etoms, pac7311, sonixj and spca561.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

drivers/media/video/gspca/etoms.c
drivers/media/video/gspca/pac7311.c
drivers/media/video/gspca/sonixj.c
drivers/media/video/gspca/spca561.c

index 6a4e682..1dbe92d 100644 (file)
@@ -461,6 +461,52 @@ static void Et_init2(struct gspca_dev *gspca_dev)
        reg_w_val(gspca_dev, 0x80, 0x20);       /* 0x20; */
 }
 
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       __u8 brightness = sd->brightness;
+
+       for (i = 0; i < 4; i++)
+               reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       int brightness = 0;
+
+       for (i = 0; i < 4; i++) {
+               reg_r(gspca_dev, ET_O_RED + i, 1);
+               brightness += gspca_dev->usb_buf[0];
+       }
+       sd->brightness = brightness >> 3;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+       __u8 contrast = sd->contrast;
+
+       memset(RGBG, contrast, sizeof(RGBG) - 2);
+       reg_w(gspca_dev, ET_G_RED, RGBG, 6);
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       int contrast = 0;
+
+       for (i = 0; i < 4; i++) {
+               reg_r(gspca_dev, ET_G_RED + i, 1);
+               contrast += gspca_dev->usb_buf[0];
+       }
+       sd->contrast = contrast >> 2;
+}
+
 static void setcolors(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -492,6 +538,16 @@ static void getcolors(struct gspca_dev *gspca_dev)
        }
 }
 
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->autogain)
+               sd->ag_cnt = AG_CNT_START;
+       else
+               sd->ag_cnt = -1;
+}
+
 static void Et_init1(struct gspca_dev *gspca_dev)
 {
        __u8 value;
@@ -614,6 +670,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->contrast = CONTRAST_DEF;
        sd->colors = COLOR_DEF;
        sd->autogain = AUTOGAIN_DEF;
+       sd->ag_cnt = -1;
        return 0;
 }
 
@@ -641,6 +698,8 @@ static void sd_start(struct gspca_dev *gspca_dev)
        else
                Et_init2(gspca_dev);
 
+       setautogain(gspca_dev);
+
        reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
        et_video(gspca_dev, 1);         /* video on */
 }
@@ -658,52 +717,6 @@ static void sd_close(struct gspca_dev *gspca_dev)
 {
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i;
-       __u8 brightness = sd->brightness;
-
-       for (i = 0; i < 4; i++)
-               reg_w_val(gspca_dev, ET_O_RED + i, brightness);
-}
-
-static void getbrightness(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i;
-       int brightness = 0;
-
-       for (i = 0; i < 4; i++) {
-               reg_r(gspca_dev, ET_O_RED + i, 1);
-               brightness += gspca_dev->usb_buf[0];
-       }
-       sd->brightness = brightness >> 3;
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
-       __u8 contrast = sd->contrast;
-
-       memset(RGBG, contrast, sizeof(RGBG) - 2);
-       reg_w(gspca_dev, ET_G_RED, RGBG, 6);
-}
-
-static void getcontrast(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i;
-       int contrast = 0;
-
-       for (i = 0; i < 4; i++) {
-               reg_r(gspca_dev, ET_G_RED + i, 1);
-               contrast += gspca_dev->usb_buf[0];
-       }
-       sd->contrast = contrast >> 2;
-}
-
 static __u8 Et_getgainG(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -733,15 +746,22 @@ static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain)
 #define LIMIT(color) \
        (unsigned char)((color > 0xff)?0xff:((color < 0)?0:color))
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void do_autogain(struct gspca_dev *gspca_dev)
 {
-       __u8 luma = 0;
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 luma;
        __u8 luma_mean = 128;
        __u8 luma_delta = 20;
        __u8 spring = 4;
-       int Gbright = 0;
+       int Gbright;
        __u8 r, g, b;
 
+       if (sd->ag_cnt < 0)
+               return;
+       if (--sd->ag_cnt >= 0)
+               return;
+       sd->ag_cnt = AG_CNT_START;
+
        Gbright = Et_getgainG(gspca_dev);
        reg_r(gspca_dev, ET_LUMA_CENTER, 4);
        g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1;
@@ -768,7 +788,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        __u8 *data,                     /* isoc packet */
                        int len)                        /* iso packet length */
 {
-       struct sd *sd;
        int seqframe;
 
        seqframe = data[0] & 0x3f;
@@ -783,13 +802,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
                                        data, 0);
                gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
-               sd = (struct sd *) gspca_dev;
-               if (sd->ag_cnt >= 0) {
-                       if (--sd->ag_cnt < 0) {
-                               sd->ag_cnt = AG_CNT_START;
-                               setautogain(gspca_dev);
-                       }
-               }
                return;
        }
        if (len) {
@@ -862,10 +874,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->autogain = val;
-       if (val)
-               sd->ag_cnt = AG_CNT_START;
-       else
-               sd->ag_cnt = -1;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
        return 0;
 }
 
@@ -889,6 +899,7 @@ static struct sd_desc sd_desc = {
        .stop0 = sd_stop0,
        .close = sd_close,
        .pkt_scan = sd_pkt_scan,
+       .dq_callback = do_autogain,
 };
 
 /* -- module initialisation -- */
index ea3d702..815bea6 100644 (file)
@@ -31,7 +31,9 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       int avg_lum;
+       int lum_sum;
+       atomic_t avg_lum;
+       atomic_t do_gain;
 
        unsigned char brightness;
        unsigned char contrast;
@@ -271,6 +273,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->contrast = CONTRAST_DEF;
        sd->colors = COLOR_DEF;
        sd->autogain = AUTOGAIN_DEF;
+       sd->ag_cnt = -1;
        return 0;
 }
 
@@ -311,6 +314,18 @@ static void setcolors(struct gspca_dev *gspca_dev)
        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 called at open time */
 static int sd_open(struct gspca_dev *gspca_dev)
 {
@@ -320,8 +335,6 @@ static int sd_open(struct gspca_dev *gspca_dev)
 
 static void sd_start(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        reg_w(gspca_dev, 0xff, 0x01);
        reg_w_buf(gspca_dev, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8);
        reg_w_buf(gspca_dev, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8);
@@ -394,6 +407,7 @@ 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) {
@@ -431,13 +445,6 @@ static void sd_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0xff, 0x01);
        reg_w(gspca_dev, 0x78, 0x04);
        reg_w(gspca_dev, 0x78, 0x05);
-
-       if (sd->autogain) {
-               sd->ag_cnt = AG_CNT_START;
-               sd->avg_lum = 0;
-       } else {
-               sd->ag_cnt = -1;
-       }
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -473,13 +480,20 @@ static void sd_close(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
 }
 
-static void setautogain(struct gspca_dev *gspca_dev, int luma)
+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 ||
@@ -523,12 +537,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 
                        /* start of frame */
                        if (sd->ag_cnt >= 0 && p > 28) {
-                               sd->avg_lum += data[p - 23];
+                               sd->lum_sum += data[p - 23];
                                if (--sd->ag_cnt < 0) {
                                        sd->ag_cnt = AG_CNT_START;
-                                       setautogain(gspca_dev,
-                                               sd->avg_lum / AG_CNT_START);
-                                       sd->avg_lum = 0;
+                                       atomic_set(&sd->avg_lum,
+                                               sd->lum_sum / AG_CNT_START);
+                                       sd->lum_sum = 0;
+                                       atomic_set(&sd->do_gain, 1);
                                }
                        }
 
@@ -677,12 +692,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->autogain = val;
-       if (val) {
-               sd->ag_cnt = AG_CNT_START;
-               sd->avg_lum = 0;
-       } else {
-               sd->ag_cnt = -1;
-       }
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
        return 0;
 }
 
@@ -706,6 +717,7 @@ static struct sd_desc sd_desc = {
        .stop0 = sd_stop0,
        .close = sd_close,
        .pkt_scan = sd_pkt_scan,
+       .dq_callback = do_autogain,
 };
 
 /* -- module initialisation -- */
index b60ff60..245a30e 100644 (file)
@@ -32,7 +32,7 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       int avg_lum;
+       atomic_t avg_lum;
        unsigned int exposure;
 
        unsigned short brightness;
@@ -781,6 +781,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->contrast = CONTRAST_DEF;
        sd->colors = COLOR_DEF;
        sd->autogain = AUTOGAIN_DEF;
+       sd->ag_cnt = -1;
+
        return 0;
 }
 
@@ -946,6 +948,22 @@ static void setcolors(struct gspca_dev *gspca_dev)
        reg_w1(gspca_dev, 0x05, data);
 }
 
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+       case SENSOR_MO4000:
+       case SENSOR_MI0360:
+               if (sd->autogain)
+                       sd->ag_cnt = AG_CNT_START;
+               else
+                       sd->ag_cnt = -1;
+               break;
+       }
+}
+
 /* -- start the camera -- */
 static void sd_start(struct gspca_dev *gspca_dev)
 {
@@ -1078,6 +1096,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
        reg_w1(gspca_dev, 0x01, reg1);
        setbrightness(gspca_dev);
        setcontrast(gspca_dev);
+       setautogain(gspca_dev);
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1124,16 +1143,23 @@ static void sd_close(struct gspca_dev *gspca_dev)
 {
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void do_autogain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       /* Thanks S., without your advice, autobright should not work :) */
        int delta;
-       int expotimes = 0;
+       int expotimes;
        __u8 luma_mean = 130;
        __u8 luma_delta = 20;
 
-       delta = sd->avg_lum;
+       /* Thanks S., without your advice, autobright should not work :) */
+       if (sd->ag_cnt < 0)
+               return;
+       if (--sd->ag_cnt >= 0)
+               return;
+       sd->ag_cnt = AG_CNT_START;
+
+       delta = atomic_read(&sd->avg_lum);
+       PDEBUG(D_FRAM, "mean lum %d", delta);
        if (delta < luma_mean - luma_delta ||
            delta > luma_mean + luma_delta) {
                switch (sd->sensor) {
@@ -1145,8 +1171,9 @@ static void setautogain(struct gspca_dev *gspca_dev)
                        sd->exposure = setexposure(gspca_dev,
                                        (unsigned int) (expotimes << 8));
                        break;
-               case SENSOR_MO4000:
-               case SENSOR_MI0360:
+               default:
+/*             case SENSOR_MO4000: */
+/*             case SENSOR_MI0360: */
                        expotimes = sd->exposure;
                        expotimes += (luma_mean - delta) >> 6;
                        if (expotimes < 0)
@@ -1159,6 +1186,8 @@ static void setautogain(struct gspca_dev *gspca_dev)
        }
 }
 
+/* scan the URB packets */
+/* This function is run at interrupt level. */
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        struct gspca_frame *frame,      /* target */
                        __u8 *data,                     /* isoc packet */
@@ -1175,9 +1204,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                                frame, data, sof + 2);
                if (sd->ag_cnt < 0)
                        return;
-               if (--sd->ag_cnt >= 0)
-                       return;
-               sd->ag_cnt = AG_CNT_START;
 /* w1 w2 w3 */
 /* w4 w5 w6 */
 /* w7 w8 */
@@ -1192,9 +1218,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 /* w5 */
                avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
                avg_lum >>= 4;
-               sd->avg_lum = avg_lum;
-               PDEBUG(D_PACK, "mean lum %d", avg_lum);
-               setautogain(gspca_dev);
+               atomic_set(&sd->avg_lum, avg_lum);
                return;
        }
        if (gspca_dev->last_packet_type == LAST_PACKET) {
@@ -1321,10 +1345,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->autogain = val;
-       if (val)
-               sd->ag_cnt = AG_CNT_START;
-       else
-               sd->ag_cnt = -1;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
        return 0;
 }
 
@@ -1348,6 +1370,7 @@ static const struct sd_desc sd_desc = {
        .stop0 = sd_stop0,
        .close = sd_close,
        .pkt_scan = sd_pkt_scan,
+       .dq_callback = do_autogain,
 };
 
 /* -- module initialisation -- */
index a261745..1073ac3 100644 (file)
@@ -644,6 +644,18 @@ static void setcontrast(struct gspca_dev *gspca_dev)
        }
 }
 
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->chip_revision == Rev072A) {
+               if (sd->autogain)
+                       sd->ag_cnt = AG_CNT_START;
+               else
+                       sd->ag_cnt = -1;
+       }
+}
+
 static void sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -671,6 +683,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
                reg_w_val(dev, 0x8500, mode);   /* mode */
                reg_w_val(dev, 0x8700, Clck);   /* 0x27 clock */
                reg_w_val(dev, 0x8112, 0x10 | 0x20);
+               setautogain(gspca_dev);
                break;
        default:
 /*     case Rev012A: */
@@ -720,18 +733,24 @@ static void sd_close(struct gspca_dev *gspca_dev)
        reg_w_val(gspca_dev->dev, 0x8114, 0);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void do_autogain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int expotimes = 0;
-       int pixelclk = 0;
-       int gainG = 0;
+       int expotimes;
+       int pixelclk;
+       int gainG;
        __u8 R, Gr, Gb, B;
        int y;
        __u8 luma_mean = 110;
        __u8 luma_delta = 20;
        __u8 spring = 4;
 
+       if (sd->ag_cnt < 0)
+               return;
+       if (--sd->ag_cnt >= 0)
+               return;
+       sd->ag_cnt = AG_CNT_START;
+
        switch (sd->chip_revision) {
        case Rev072A:
                reg_r(gspca_dev, 0x8621, 1);
@@ -795,18 +814,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        __u8 *data,             /* isoc packet */
                        int len)                /* iso packet length */
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        switch (data[0]) {
        case 0:         /* start of frame */
                frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
                                        data, 0);
-               if (sd->ag_cnt >= 0) {
-                       if (--sd->ag_cnt < 0) {
-                               sd->ag_cnt = AG_CNT_START;
-                               setautogain(gspca_dev);
-                       }
-               }
                data += SPCA561_OFFSET_DATA;
                len -= SPCA561_OFFSET_DATA;
                if (data[1] & 0x10) {
@@ -944,10 +955,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->autogain = val;
-       if (val)
-               sd->ag_cnt = AG_CNT_START;
-       else
-               sd->ag_cnt = -1;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
        return 0;
 }
 
@@ -971,6 +980,7 @@ static const struct sd_desc sd_desc = {
        .stop0 = sd_stop0,
        .close = sd_close,
        .pkt_scan = sd_pkt_scan,
+       .dq_callback = do_autogain,
 };
 
 /* -- module initialisation -- */