Commit cebf3b67 authored by Jean-Francois Moine's avatar Jean-Francois Moine Committed by Mauro Carvalho Chehab

V4L/DVB (8604): gspca: Fix of "scheduling while atomic" crash.

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: default avatarJean-Francois Moine <moinejf@free.fr>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 594f5b8b
......@@ -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 -- */
......
......@@ -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 -- */
......
......@@ -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 -- */
......
......@@ -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 -- */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment