[media] gspca - jeilinj: suppress workqueue

Signed-off-by: Patrice CHOTARD <patricechotard@free.fr>
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Patrice Chotard 2011-04-18 17:37:06 -03:00 committed by Mauro Carvalho Chehab
parent ec2a095435
commit c3d8692758

View File

@ -5,6 +5,7 @@
* download raw JPEG data.
*
* Copyright (C) 2009 Theodore Kilgore
* Copyright (C) 2011 Patrice Chotard
*
* 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
@ -23,7 +24,6 @@
#define MODULE_NAME "jeilinj"
#include <linux/workqueue.h>
#include <linux/slab.h>
#include "gspca.h"
#include "jpeg.h"
@ -38,25 +38,23 @@ MODULE_LICENSE("GPL");
/* Maximum transfer size to use. */
#define JEILINJ_MAX_TRANSFER 0x200
#define FRAME_HEADER_LEN 0x10
#define FRAME_START 0xFFFFFFFF
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
int blocks_left;
const struct v4l2_pix_format *cap_mode;
/* Driver stuff */
struct work_struct work_struct;
struct workqueue_struct *work_thread;
u8 quality; /* image quality */
u8 jpegqual; /* webcam quality */
u8 jpeg_hdr[JPEG_HDR_SZ];
};
struct jlj_command {
unsigned char instruction[2];
unsigned char ack_wanted;
};
struct jlj_command {
unsigned char instruction[2];
unsigned char ack_wanted;
};
/* AFAICT these cameras will only do 320x240. */
static struct v4l2_pix_format jlj_mode[] = {
@ -107,6 +105,7 @@ static int jlj_start(struct gspca_dev *gspca_dev)
int i;
int retval = -1;
u8 response = 0xff;
struct sd *sd = (struct sd *) gspca_dev;
struct jlj_command start_commands[] = {
{{0x71, 0x81}, 0},
{{0x70, 0x05}, 0},
@ -136,6 +135,8 @@ static int jlj_start(struct gspca_dev *gspca_dev)
{{0x71, 0x80}, 0},
{{0x70, 0x07}, 0}
};
sd->blocks_left = 0;
for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
retval = jlj_write2(gspca_dev, start_commands[i].instruction);
if (retval < 0)
@ -149,102 +150,47 @@ static int jlj_start(struct gspca_dev *gspca_dev)
return retval;
}
static int jlj_stop(struct gspca_dev *gspca_dev)
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, int len)
{
int i;
int retval;
struct jlj_command stop_commands[] = {
{{0x71, 0x00}, 0},
{{0x70, 0x09}, 0},
{{0x71, 0x80}, 0},
{{0x70, 0x05}, 0}
};
for (i = 0; i < ARRAY_SIZE(stop_commands); i++) {
retval = jlj_write2(gspca_dev, stop_commands[i].instruction);
if (retval < 0)
return retval;
}
return retval;
}
/* This function is called as a workqueue function and runs whenever the camera
* is streaming data. Because it is a workqueue function it is allowed to sleep
* so we can use synchronous USB calls. To avoid possible collisions with other
* threads attempting to use the camera's USB interface the gspca usb_lock is
* used when performing the one USB control operation inside the workqueue,
* which tells the camera to close the stream. In practice the only thing
* which needs to be protected against is the usb_set_interface call that
* gspca makes during stream_off. Otherwise the camera doesn't provide any
* controls that the user could try to change.
*/
static void jlj_dostream(struct work_struct *work)
{
struct sd *dev = container_of(work, struct sd, work_struct);
struct gspca_dev *gspca_dev = &dev->gspca_dev;
int blocks_left; /* 0x200-sized blocks remaining in current frame. */
int act_len;
struct sd *sd = (struct sd *) gspca_dev;
int packet_type;
int ret;
u8 *buffer;
u32 header_marker;
buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
err("Couldn't allocate USB buffer");
goto quit_stream;
PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
len, JEILINJ_MAX_TRANSFER);
if (len != JEILINJ_MAX_TRANSFER) {
PDEBUG(D_PACK, "bad length");
goto discard;
}
while (gspca_dev->present && gspca_dev->streaming) {
/*
* Now request data block 0. Line 0 reports the size
* to download, in blocks of size 0x200, and also tells the
* "actual" data size, in bytes, which seems best to ignore.
*/
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x82),
buffer, JEILINJ_MAX_TRANSFER, &act_len,
JEILINJ_DATA_TIMEOUT);
PDEBUG(D_STREAM,
"Got %d bytes out of %d for Block 0",
act_len, JEILINJ_MAX_TRANSFER);
if (ret < 0 || act_len < FRAME_HEADER_LEN)
goto quit_stream;
blocks_left = buffer[0x0a] - 1;
PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
/* check if it's start of frame */
header_marker = ((u32 *)data)[0];
if (header_marker == FRAME_START) {
sd->blocks_left = data[0x0a] - 1;
PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
/* Start a new frame, and add the JPEG header, first thing */
gspca_frame_add(gspca_dev, FIRST_PACKET,
dev->jpeg_hdr, JPEG_HDR_SZ);
sd->jpeg_hdr, JPEG_HDR_SZ);
/* Toss line 0 of data block 0, keep the rest. */
gspca_frame_add(gspca_dev, INTER_PACKET,
buffer + FRAME_HEADER_LEN,
data + FRAME_HEADER_LEN,
JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
while (blocks_left > 0) {
if (!gspca_dev->present)
goto quit_stream;
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x82),
buffer, JEILINJ_MAX_TRANSFER, &act_len,
JEILINJ_DATA_TIMEOUT);
if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER)
goto quit_stream;
PDEBUG(D_STREAM,
"%d blocks remaining for frame", blocks_left);
blocks_left -= 1;
if (blocks_left == 0)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
gspca_frame_add(gspca_dev, packet_type,
buffer, JEILINJ_MAX_TRANSFER);
}
}
quit_stream:
mutex_lock(&gspca_dev->usb_lock);
if (gspca_dev->present)
jlj_stop(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
kfree(buffer);
} else if (sd->blocks_left > 0) {
PDEBUG(D_STREAM, "%d blocks remaining for frame",
sd->blocks_left);
sd->blocks_left -= 1;
if (sd->blocks_left == 0)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
gspca_frame_add(gspca_dev, packet_type,
data, JEILINJ_MAX_TRANSFER);
} else
goto discard;
return;
discard:
/* Discard data until a new frame starts. */
gspca_dev->last_packet_type = DISCARD_PACKET;
}
/* This function is called at probe time just before sd_init */
@ -255,31 +201,50 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *dev = (struct sd *) gspca_dev;
dev->quality = 85;
dev->jpegqual = 85;
PDEBUG(D_PROBE,
"JEILINJ camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam->cam_mode = jlj_mode;
cam->nmodes = 1;
cam->bulk = 1;
/* We don't use the buffer gspca allocates so make it small. */
cam->bulk_size = 32;
INIT_WORK(&dev->work_struct, jlj_dostream);
cam->bulk_nurbs = 1;
cam->bulk_size = JEILINJ_MAX_TRANSFER;
return 0;
}
/* called on streamoff with alt==0 and on disconnect */
/* the usb_lock is held at entry - restore on exit */
static void sd_stop0(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *dev = (struct sd *) gspca_dev;
int i;
u8 *buf;
u8 stop_commands[][2] = {
{0x71, 0x00},
{0x70, 0x09},
{0x71, 0x80},
{0x70, 0x05}
};
/* wait for the work queue to terminate */
mutex_unlock(&gspca_dev->usb_lock);
/* This waits for jlj_dostream to finish */
destroy_workqueue(dev->work_thread);
dev->work_thread = NULL;
mutex_lock(&gspca_dev->usb_lock);
for (;;) {
/* get the image remaining blocks */
usb_bulk_msg(gspca_dev->dev,
gspca_dev->urb[0]->pipe,
gspca_dev->urb[0]->transfer_buffer,
JEILINJ_MAX_TRANSFER, NULL,
JEILINJ_DATA_TIMEOUT);
/* search for 0xff 0xd9 (EOF for JPEG) */
i = 0;
buf = gspca_dev->urb[0]->transfer_buffer;
while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
i++;
if (i != (JEILINJ_MAX_TRANSFER - 1))
/* last remaining block found */
break;
}
for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
jlj_write2(gspca_dev, stop_commands[i]);
}
/* this function is called at probe and resume time */
@ -304,10 +269,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
PDEBUG(D_ERR, "Start streaming command failed");
return ret;
}
/* Start the workqueue function to do the streaming */
dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
queue_work(dev->work_thread, &dev->work_struct);
return 0;
}
@ -325,7 +286,8 @@ static const struct sd_desc sd_desc = {
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stop0 = sd_stop0,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
};
/* -- device connect -- */